home *** CD-ROM | disk | FTP | other *** search
- name bufptr
- title Buffered Printer Driver for MSDOS 3.X
- subttl Equates
- page 60,132
- ;
- comment ~
- Placed in the public domain by
- Clayton Haapala
- Lee Data Corporation
- Eden Prairie, MN 55344
- (612)828-0460
- December 5, 1985
- Non-commercial use only, please!
-
- This device driver replaces the drivers for PRN and LPT1, and creates
- the alternate name BPTR. This driver gives you a 16k buffer for
- printing, which is drained by the parallel printer interrupts, IRQ7. I
- have also added IOCTL support for the DOS 3.X Output Till Busy call,
- but I'm not sure that it works well, since one needs a PRINT program
- that will make use of it, which I don't have yet. There is also
- another IOCTL call to flush that printer buffer; currently, this is the
- only private IOCTL command supported. The PFLUSH.C program make this
- call. I think this program is a fairly reasonable example of a
- character device driver for MSDOS and I hope you find it usable and
- informative.
-
- Caveats: BUFPRN does not catch any interrupt 17H calls, such has BASIC
- makes when initializing LPT1. BASIC will cause the parallel port to be
- re-initialized, turning off interrupts on the chip. Upon the next time
- you use the driver from DOS, then, you may get an Abort, Retry, or
- Ignore message. Just say "Retry" and you'll hear the printer
- re-initialize again and continue to print.
-
- Original IBM-PCs had an error in the motherboard logic that did not
- allow IRQ7 to be honored, even though all chips and ports were enabled.
- The IBM XT works fine, however, as do many compatibles.
- ~
- ;
- ; Added Output Till Busy handling. Error checking becomes a little more
- ; obscure. If no room can be found in the buffer within OTB_retries
- ; times through the driver, then report an error, instead of doing a direct
- ; time-out loop.
-
- req_hdr struc
- rlen db ?
- db ?
- rcmd db ?
- rstatus dw ?
- db 8 dup (?) ; Reserved
- db ?
- rtrans dd ?
- rcount dw ?
- req_hdr ends
-
- stat_word record ERR:1,RES:5,BSY:1,DUN:1,ERRCD:8
-
- bios_data segment at 40H
- dw 4 dup (?)
- printer_base dw 4 dup (?)
- bios_data ends
-
- kelvin segment at 0
- org 0Fh*4
- prn_hvec label dword
- kelvin ends
-
- OTB_retries equ 10
-
- subttl Data Declarations
- page
- code segment
-
- assume CS:code,DS:nothing,ES:nothing
-
- dev_header:
- dd dev2 ; Pointer to next device
- dw 0E000H ; Character device, IOCTL, OutTilBusy
- dw strategy ; Pointer to strategy routine
- dw int_entry ; Pointer to "interrupt" routine
- db 'PRN ' ; Device name
-
- dev2 label far
- dd dev3 ; Pointer to next device
- dw 0E000H ; Character device, IOCTL, OutTilBusy
- dw strategy ; Pointer to strategy routine
- dw int_entry ; Pointer to "interrupt" routine
- db 'LPT1 ' ; Device name
-
- dev3 label far
- dd -1 ; Pointer to next device
- dw 0E000H ; Character device, IOCTL, OutTilBusy
- dw strategy ; Pointer to strategy routine
- dw int_entry ; Pointer to "interrupt" routine
- db 'BPTR ' ; Device name
-
- req_ptr dd 0 ; Pointer to request header
-
- dispatch label word
- dw init, media, bpb, ioctlin
- dw input, inkey, instat, inflush
- dw output, outverf, outstat, outflush
- dw ioctlout, devopen, devclose, remov_media
- dw outtilbusy
- numcmds equ ($ - dispatch) / 2
- softint dw 0 ; True if a soft int to 0F has been done
- dev_addr dw 0 ; Base address of first printer
- bufsize dw 4000h ; Size of buffer
- bufmsk dw 4000h-1 ; For wrapping pointers
- last_err dw 0 ; Error code from prn_int
- count dw 0 ; Number of bytes in buffer
- inpt dw 0 ; Pointer to next avail free byte
- outpt dw 0 ; Pointer to next char to print
- flushed dw -1 ; True if interrupt with no data
- busyout db 0 ; True if OutputTilBusy
- found_full db OTB_retries ; Error when reaches 0, reset if a
- ; byte successfully output
- subttl Procedures
- page
- strategy proc far
- ; Store ES:BX in req_ptr
- mov word ptr req_ptr,BX
- mov word ptr req_ptr+2,ES
- ret
- strategy endp
-
- assume DS:code
-
- int_entry proc far
- ; Called from DOS, this procedure dispatches control to the appropriate
- ; routine. On return to int_done, the done bit is set in the status word.
- push AX
- push BX
- push CX
- push DX
- push SI
- push DI
- push DS
- push ES
-
- push CS
- pop DS
- les SI,req_ptr
- mov BL,ES:[SI].rcmd
- cmp BL,numcmds ; Command within range?
- jbe int_ok ; Fine
- mov ES:[SI].rstatus,8003H ; Unknown command
- jmp short int_done
- int_ok:
- xor BH,BH
- shl BX,1 ; Make word offset
- jmp dispatch[BX] ; Go do it
- int_done:
- les SI,req_ptr
- or ES:[SI].rstatus,mask DUN
- pop ES
- pop DS
- pop DI
- pop SI
- pop DX
- pop CX
- pop BX
- pop AX
- ret
- int_entry endp
-
- ; Unsupported command routines:
- media:
- bpb:
- ioctlin:
- input:
- inkey:
- instat:
- inflush:
- devopen:
- devclose:
- remov_media:
- mov ES:[SI].rstatus,8003h ; Bad command
- jmp int_done
-
- ioctlout:
- ; ES:SI points to request header
- ; First byte must be an escape, the next must be one of the following:
- ; 0FFH -- flush buffer
- les SI,ES:[SI].rtrans ; Where the bytes are
- mov AX,ES:[SI] ; Get first two bytes, only
- cmp AX,0FF1Bh ; ESC - FF?
- je outflush ; Flush the buffer and leave
- jmp int_done ; Ignore anything else
-
- outstat:
- ; Return last_err if non-zero, else return busy if buffer is full
- xor AX,AX
- xchg AX,last_err
- or AX,AX ; Non-zero?
- jz outs10 ; No
- or AH,80H ; Turn error bit on
- mov ES:[SI].rstatus,AX
- jmp int_done
- outs10:
- mov AX,count
- cmp AX,bufsize ; Buffer full?
- jb outs20 ; No
- mov ES:[SI].rstatus,mask BSY
- outs20:
- jmp int_done
-
- outflush:
- ; Flush the output buffer
- xor AX,AX
- mov count,AX
- mov inpt,AX
- mov outpt,AX
- mov last_err,AX
- jmp int_done
-
- outtilbusy:
- mov busyout,1 ; Make True
- outverf:
- output:
- ; Output chars to the buffer. A time-out check is performed for each char,
- ; so it is an error if no space in the buffer opens up in time. All chars
- ; are output until no more or last_err goes non-zero.
- ; COUNT is the number of chars in the buffer.
- ; INPT points to the next place to add a char.
-
- ; First check to see that the printer has not been "uninitialized", that is,
- ; see that interrupts have not been turned off.
- mov DX,dev_addr
- add DX,2
- in AL,DX ; Get current init parms
- test AL,10h ; Interrupts enabled?
- jnz out5 ; Yes, no problem
- ; Re-initialize, should happen only once at first output...
- mov AL,8 ; Select printer
- out DX,AL
- mov AX,2000
- out2: dec AX
- jnz out2 ; Delay
- mov AL,1Ch ; Ints on, init high, no ALF
- out DX,AL
- out5:
- mov CX,ES:[SI].rcount ; How many to output
- les SI,ES:[SI].rtrans ; Where the chars are
- out10:
- mov AL,ES:[SI] ; Get a char
- inc SI
- out15:
- mov BL,10 ; Set up for time-out
- out20:
- xor DX,DX
- out30:
- cmp last_err,0 ; Any hard error?
- jnz out34 ; Yes
- mov DI,count
- cmp DI,bufsize ; Any room?
- jb out40 ; Yes
- ;
- ; If we are Outputting Until Busy, check if we have found no room for
- ; OTB_retries times. Error if so, return actual output if not.
- cmp busyout,0 ; Output Til Busy?
- jz out32 ; No
- dec found_full ; Been full too often?
- jz out34 ; Yes, report error
- sub ES:[SI].rcount,CX ; Report bytes output
- jmp int_done ; Outputted until busy...
- out32:
- ; Decrement loop counter -- we'll wait a while for some room, since we're
- ; not Outputting until Busy like above.
- dec DX
- jnz out30
- dec BL
- jnz out20
- mov softint,-1 ; Check the status
- int 0Fh ; What's going on?
- cmp last_err,0 ; Anything? Maybe jump start...
- jz out15 ; Try again
- out34:
- les SI,req_ptr
- mov AX,last_err
- or AH,80h ; Set error bit
- mov ES:[SI].rstatus,AX
- sub ES:[SI].rcount,CX ; # of bytes output
- mov last_err,0 ; Zero out for next time
- jmp int_done
- out40:
- ; Output a byte to the buffer, start printing if necessary. Reset found_full
- ; to OTB_retries to show that all is OK.
-
- mov found_full,OTB_retries ; Reset error count
- cli
- mov DI,inpt
- mov buffer[DI],AL ; Store char
- inc count ; Update
- inc DI
- and DI,bufmsk ; Wrap, maybe
- mov inpt,DI ; Restore pointer
- sti
- xor AX,AX
- xchg AX,flushed
- or AX,AX ; Need to jump start?
- jz out50 ; No
- mov softint,-1 ; Say "no EOI"
- int 0Fh ; Start things going...
- out50:
- loop o_again ; Do it for the next char
- ; Everything output without a hitch...the rcount IS the number of bytes
- ; output.
- jmp int_done
- o_again: ; One more time (at least)
- jmp out10
-
- assume DS:code
-
- prn_int proc far
- ; Hard interrupt handler for vector 0Fh. Check the status and then pull
- ; a byte from the buffer if all is well. If bad status then set last_err
- ; to the proper error code. If the buffer is empty, set flushed to TRUE
- ; so the next output will start the interrupts going again. If the variable
- ; SOFTINT is true, don't perform an EOI at the end, because the driver has
- ; initiated this interrupt, not the hardware.
-
- sti
- push AX
- push DX
- push SI
- push DS
-
- push CS
- pop DS
-
- ; Check for bad status
- mov DX,dev_addr
- inc DX ; Get status port
- in AL,DX ; Read status
- xor AL,48h ; Flip a couple bits
- test AL,28h ; Any errors?
- jz pok ; Ok to print
- mov SI,9
- test AL,20h ; Paper out?
- jnz p10 ; Yes
- mov SI,2 ; Say "device not ready"
- p10:
- mov last_err,SI ; Store error code
- jmp short p99
- pok:
- ; Get char from the buffer
- cmp count,0
- jnz p20 ; Buffer not empty
- mov flushed,-1
- jmp short p99
- p20:
- mov SI,outpt
- mov AL,buffer[SI] ; Get char
- dec count ; Update counter and
- inc SI ; pointer
- and SI,bufmsk
- mov outpt,SI
-
- ; Output the character and strobe the printer
-
- dec DX ; Data port
- out DX,AL ; Output the byte
- add DX,2 ; Strobe port
- in AL,DX
- or AL,1 ; Set strobe bit high
- out DX,AL
- and AL,0FEh ; Set strobe bit low
- out DX,AL
- p99:
- ; Exit interrupt handler
- xor AX,AX
- xchg AX,softint
- or AX,AX ; A soft interrupt?
- cli
- jnz p100 ; Yes, no EOI
- mov AL,20h
- out 20h,AL ; Do EOI
- p100:
- pop DS
- pop SI
- pop DX
- pop AX
- iret
- prn_int endp
-
- buffer label byte
- been_here db 0
-
- init:
- ; Perform device initialization
- push DS
-
- assume DS:kelvin
-
- cmp been_here,1
- je fnd_end ; Don't re-init
-
- xor AX,AX
- mov DS,AX
- mov word ptr prn_hvec,offset prn_int
- mov word ptr prn_hvec+2,CS
- mov AX,40h
- mov DS,AX
-
- assume DS:bios_data
-
- mov DX,printer_base ; Address of first printer
- mov dev_addr,DX ; Store locally
- add DX,2
- mov AL,8 ; Select printer
- out DX,AL
- mov AX,2000
- init10: dec AX
- jnz init10 ; Delay
- mov AL,1Ch ; Ints on, init high, no ALF
- out DX,AL
-
- assume DS:code
-
- fnd_end:
- pop DS
- mov AX,bufsize
- add AX,offset buffer
- inc AX ; End of driver addres + 1
- mov ES:[SI+14],AX ; Offset of end of driver
- mov ES:[SI+16],CS ; Segment of end of driver
- in AL,21h ; Get interrupt mask
- and AL,7Fh ; Enable IRQ7
- out 21h,AL
- jmp int_done
-
- code ends
- ;
- end